﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Windows.Threading;

namespace Xceed.Silverlight.Data
{
  internal class DataSourceWrapper
  {
    internal DataSourceWrapper( DataSource dataSource )
    {
      if( dataSource == null )
        throw new ArgumentNullException( "dataSource" );

      m_dataSource = dataSource;
    }

    public DataSourceWrapper( Dispatcher dispatcher, DataSourceProvider provider )
      : this( new DataSource( dispatcher, provider ) )
    {
    }

    public event EventHandler<SourceChangedEventArgs> SourceChanged
    {
      add
      {
        if( m_sourceChangedHandler == null )
        {
          m_dataSource.SourceChanged += OnSourceChanged;
        }

        m_sourceChangedHandler += value;
      }
      remove
      {

        m_sourceChangedHandler -= value;

        if( m_sourceChangedHandler == null )
        {
          m_dataSource.SourceChanged -= OnSourceChanged;
        }
      }
    }

    public event EventHandler<ExpansionChangedEventArgs> ExpansionChanged
    {

      add
      {
        if( m_expansionChangedHandler == null )
        {
          m_dataSource.ExpansionChanged += OnExpansionChanged;
        }

        m_expansionChangedHandler += value;
      }
      remove
      {

        m_expansionChangedHandler -= value;

        if( m_expansionChangedHandler == null )
        {
          m_dataSource.ExpansionChanged -= OnExpansionChanged;
        }
      }
    }

    public Type ElementType
    {
      get
      {
        return m_dataSource.ElementType;
      }
    }

    public DataSourceProvider Provider
    {
      get
      {
        return m_dataSource.Provider;

      }
    }

    protected DataSource DataSource
    {
      get
      {
        return m_dataSource;
      }
    }

    #region DataHelper Internal Property

    internal IDataSourceWrapperHelper DataHelper
    {
      get
      {
        if( m_dataSourceWrapperHelper == null )
        {
          m_dataSourceWrapperHelper = new DataSourceWrapperDataHelper( m_dataSource.DataHelper );
        }

        return m_dataSourceWrapperHelper;
      }
    }

    private IDataSourceWrapperHelper m_dataSourceWrapperHelper = null;

    #endregion

    internal IEnumerable<SortDescription> PrimaryKeyInfo
    {
      get
      {
        return m_dataSource.PrimaryKeyInfo;
      }
    }

    public Predicate<object> Filter
    {
      get
      {
        return m_dataSource.Filter;
      }
      set
      {
        m_dataSource.Filter = value;
      }
    }

    public FilterExpressionBase FilterExpression
    {
      get
      {
        return m_dataSource.FilterExpression;
      }
      set
      {
        m_dataSource.FilterExpression = value;
      }
    }

    public SortDescription[] AppliedSortDescriptions
    {
      get
      {
        return m_dataSource.CurrentSortOrder.ToArray();
      }
    }

    public SortDescriptionCollection SortDescriptions
    {
      get
      {
        return m_dataSource.SortDescriptions;
      }
    }

    public ObservableCollection<GroupDescription> GroupDescriptions
    {
      get
      {
        return m_dataSource.GroupDescriptions;
      }
    }

    #region IsPrimaryKeyVolatile Property

    internal bool IsPrimaryKeyVolatile
    {
      get
      {
        return m_dataSource.IsPrimaryKeyVolatile;
      }
    }

    #endregion IsPrimaryKeyVolatile Property

    #region GroupHeadersFootersConfigurationProvider

    public IGroupHeadersFootersSelector GroupHeadersFootersSelector
    {
      get
      {
        return m_dataSource.GroupHeadersFootersSelector;
      }

      set
      {
        m_dataSource.GroupHeadersFootersSelector = value;
      }
    }

    #endregion GroupHeadersFootersConfigurationProvider

    #region IGroupStatFunctionsSelector

    public IGroupStatFunctionsSelector GroupStatFunctionsSelector
    {
      get
      {
        return m_dataSource.GroupStatFunctionsSelector;
      }

      set
      {
        m_dataSource.GroupStatFunctionsSelector = value;
      }
    }

    #endregion IGroupStatFunctionsSelector

    public DataPath CreateDataPathForObject( object item )
    {
      if( item == null )
        throw new ArgumentNullException( "item" );

      //DataHelper will create a "full length" DataPath based on the current Grouping of the DataSource.
      return m_dataSource.DataHelper.DeepConvert( item );
    }

    public DataPath CreateDataPathForArray( object[] array )
    {
      if( array == null )
        throw new ArgumentNullException( "array" );

      if( array.Rank != 1 )
        throw new ArgumentException( "The specified array must be unidimensional.", "array" );

      int length = array.Length;

      if( length == 0 )
        throw new ArgumentException( "The specified array cannot be empty.", "array" );

      int groupDescriptionsCount = m_dataSource.GroupDescriptions.Count;

      if( length > ( groupDescriptionsCount + 1 ) )
        throw new ArgumentException( "The specified array contains more elements than the maximum depth of DataPath.", "array" ); 

      DataItem[] dataItems = new DataItem[ length ];

      for( int i = 0; i < length; i++ )
      {
        DataItem dataItem = null;
        if( i < groupDescriptionsCount )
        {
          dataItem = new GroupDataItem( array[ i ] );
        }
        else
        {
          dataItem = m_dataSource.UserObjectConverter.Invoke( array[ i ] );
        }
        Debug.Assert( dataItem != null );

        dataItems[ i ] = dataItem;
      }

      return new DataPath( dataItems );
    }

    public object GetDataObjectPropertyValue( string propertyName, DataItem referenceObject )
    {
      return m_dataSource.GetDataObjectPropertyValue( propertyName, referenceObject );
    }

    public DataSourceEnumeratorWrapper GetSyncDataEnumerator()
    {
      return new DataSourceEnumeratorWrapper( m_dataSource.GetSyncDataEnumerator() );
    }

    public void InvalidateData()
    {
      m_dataSource.InvalidateData();
    }

    public void InvalidateDataPath( DataPath dataPath )
    {
      m_dataSource.InvalidateDataPath( dataPath );
    }

    public IEnumerable<FilterOperator> GetSupportedFilterOperators( Type dataType )
    {
      return m_dataSource.GetSupportedFilterOperators( dataType );
    }

    public bool IsGroupPath( DataPath dataPath )
    {
      return m_dataSource.GroupHelper.IsGroupPath( dataPath );
    }

    public bool IsGroupExpanded( int level, DataPath group )
    {
      return !m_dataSource.CollapsedGroupCollections.IsCollapsedAtTargetLevel( group, level + 1 );
    }

    public int GetCollapsedDepth( DataPath path )
    {
      int depth = m_dataSource.CollapsedGroupCollections.GetCollapsedDepth( path );
      return ( depth == 0 ) ? -1 : depth;
    }

    public bool IsGroupExpanded( DataPath dataPath, out DataPath collapsedGroupPath )
    {
      collapsedGroupPath = null;

      bool isGroupExpanded = true;
      DataPath parentGroupPath = dataPath.ParentPath;

      //If not on the top group.
      if( parentGroupPath != null )
      {
        //Recurse.
        isGroupExpanded = this.IsGroupExpanded( parentGroupPath, out collapsedGroupPath );
      }

      //If no collapsed group found so far.
      if( isGroupExpanded )
      {
        int level = dataPath.Depth - 1;

        //Verify if the current group is collapsed.
        isGroupExpanded = !( dataPath[ level ] is GroupDataItem )
                        || ( this.IsGroupExpanded( level, dataPath ) );

        //If so, keep a reference to the group.
        if( !isGroupExpanded )
        {
          collapsedGroupPath = dataPath;
        }
      }

      return isGroupExpanded;
    }

    public void ExpandGroup( int level, DataPath group )
    {
      m_dataSource.CollapsedGroupCollections[ level ].Expand( group );
    }

    public void CollapseGroup( int level, DataPath group )
    {
      m_dataSource.CollapsedGroupCollections[ level ].Collapse( group );
    }

    public DataRangeContext GetCurrentDataRangeContext()
    {
      return m_dataSource.GetCurrentDataRangeContext();
    }

    public IDataRangeEnumerator GetDataRangeEnumerator( IEnumerable<DataRange> ranges )
    {
      return m_dataSource.GetDataRangeEnumerator( ranges );
    }

    public IDataRangeEnumerator GetDataRangeEnumerator(
      IEnumerable<DataRange> ranges,
      IEnumerable<GroupDescription> groupDescriptions,
      IEnumerable<SortDescription> sortDescriptions )
    {
      return m_dataSource.GetDataRangeEnumerator( ranges, groupDescriptions, sortDescriptions );
    }

    private void OnSourceChanged( object sender, SourceChangedEventArgs e )
    {
      m_sourceChangedHandler( this, e );
    }

    private void OnExpansionChanged( object sender, ExpansionChangedEventArgs e )
    {
      m_expansionChangedHandler( this, e );
    }

    private DataSource m_dataSource;//null
    private EventHandler<SourceChangedEventArgs> m_sourceChangedHandler; //null
    private EventHandler<ExpansionChangedEventArgs> m_expansionChangedHandler; //null

    public static object ConvertIfNeeded( object newItemsSource )
    {
      if( newItemsSource is DataSource )
      {
        newItemsSource = new DataSourceWrapper( ( DataSource )newItemsSource );
      }
      return newItemsSource;
    }
  }
}
